/*
 * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package test.time

import kotlin.math.absoluteValue
import kotlin.test.*
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Instant

class InstantIsoStringsTest {

    @Test
    fun parseDates() {
        fun Int.zeroPadded(digits: Int): String = when {
            this >= 0 -> toString().padStart(digits, '0')
            else -> "-${absoluteValue.toString().padStart(digits, '0')}"
        }

        fun localDateToString(year: Int, month: Int, day: Int) =
            "${year.zeroPadded(4)}-${month.zeroPadded(2)}-${day.zeroPadded(2)}"

        // only works for 1-4-digit years
        fun assertMonthBoundariesAreCorrect(year: Int, month: Int, lastDayOfMonth: Int) {
            val validString = "${localDateToString(year, month, lastDayOfMonth)}T23:59:59Z"
            val invalidString = "${localDateToString(year, month, lastDayOfMonth + 1)}T23:59:59Z"
            Instant.parse(validString) // shouldn't throw
            assertNotNull(Instant.parseOrNull(validString))
            assertInvalidFormat(invalidString) { Instant.parse(invalidString) }
            assertNull(Instant.parseOrNull(invalidString))
        }

        val nonLeapYears = listOf(
            1970, 1971, 1973, 1974, 1975, 2021, 2022, 2023, 2100, 1100, 1, 2, 3, 5, -1, -2, -1971, 100, -100
        )
        val leapYears = listOf(
            0, 1972, 1976, 1980, 2000, 1200, 400, -400, -4, -8,
        )
        for ((month, lastDayOfMonth) in arrayOf(
            1 to 31, 3 to 31, 4 to 30, 5 to 31, 6 to 30,
            7 to 31, 8 to 31, 9 to 30, 10 to 31, 11 to 30, 12 to 31,
        )) {
            for (year in nonLeapYears + leapYears) {
                assertMonthBoundariesAreCorrect(year, month, lastDayOfMonth)
            }
        }
        for (leapYear in leapYears) {
            assertMonthBoundariesAreCorrect(leapYear, 2, 29)
        }
        for (nonLeapYear in nonLeapYears) {
            assertMonthBoundariesAreCorrect(nonLeapYear, 2, 28)
        }
    }

    @Test
    fun parseIsoString() {
        for ((str, seconds, nanos) in arrayOf<Triple<String, Long, Int>>(
            // all components are taken into account
            Triple("1970-01-01T00:00:00Z", 0, 0),
            Triple("1970-01-01T00:00:00.000000001Z", 0, 1),
            Triple("1970-01-01T00:00:00.100Z", 0, 100000000),
            Triple("1970-01-01T00:00:01Z", 1, 0),
            Triple("1970-01-01T00:01:00Z", 60, 0),
            Triple("1970-01-01T00:01:01Z", 61, 0),
            Triple("1970-01-01T00:01:01.000000001Z", 61, 1),
            Triple("1970-01-01T01:00:00Z", 3600, 0),
            Triple("1970-01-01T01:01:01.000000001Z", 3661, 1),
            Triple("1970-01-02T01:01:01.100Z", 90061, 100000000),
            Triple("1970-02-02T01:01:01.100Z", 31 * 86400 + 90061, 100000000),
            Triple("1971-02-02T01:01:01.100Z", (365 + 31) * 86400 + 90061, 100000000),
            // how many digits get output for various precision of the sub-second portion
            Triple("1970-01-01T00:00:00.100Z", 0, 100_000_000),
            Triple("1970-01-01T00:00:00.010Z", 0, 10_000_000),
            Triple("1970-01-01T00:00:00.001Z", 0, 1_000_000),
            Triple("1970-01-01T00:00:00.000100Z", 0, 100_000),
            Triple("1970-01-01T00:00:00.000010Z", 0, 10_000),
            Triple("1970-01-01T00:00:00.000001Z", 0, 1_000),
            Triple("1970-01-01T00:00:00.000000100Z", 0, 100),
            Triple("1970-01-01T00:00:00.000000010Z", 0, 10),
            Triple("1970-01-01T00:00:00.000000001Z", 0, 1),
            // random data queried from java.time
            Triple("+51861-09-21T11:07:43.782719883Z", 1574430692863, 782719883),
            Triple("+395069-04-30T01:28:37.454777349Z", 12405016603717, 454777349),
            Triple("-551259-03-05T08:01:36.195722269Z", -17458215523104, 195722269),
            Triple("+498403-02-11T17:47:05.156642423Z", 15665915958425, 156642423),
            Triple("+283686-10-14T23:00:25.666521845Z", 8890123158025, 666521845),
            Triple("-910329-04-04T09:27:54.456784744Z", -28789367639526, 456784744),
            Triple("-37222-03-21T18:04:37.006055123Z", -1236773166923, 6055123),
            Triple("-189377-03-30T01:37:14.288808090Z", -6038320515766, 288808090),
            Triple("-67394-03-24T03:19:41.794404047Z", -2188909341619, 794404047),
            Triple("-870649-05-27T13:47:39.925150102Z", -27537183223941, 925150102),
            Triple("+94020-04-10T14:51:21.569206089Z", 2904826114281, 569206089),
            Triple("-945485-07-11T23:28:58.240153828Z", -29898775384262, 240153828),
            Triple("-73722-02-22T11:19:54.364548772Z", -2388604250406, 364548772),
            Triple("-645899-05-17T16:44:21.522135477Z", -20444759104539, 522135477),
            Triple("-702594-10-20T10:13:53.212104714Z", -22233867083167, 212104714),
            Triple("-442579-11-22T01:35:44.591216727Z", -14028583357456, 591216727),
            Triple("-849915-06-25T01:28:27.625015449Z", -26882878833093, 625015449),
            Triple("-481897-08-13T05:44:47.077814711Z", -15269348340913, 77814711),
            Triple("+295919-02-07T15:47:37.850981753Z", 9276137682457, 850981753),
            Triple("+967334-01-15T15:08:10.235167075Z", 30463946694490, 235167075),
            Triple("+774237-04-30T16:00:32.810606451Z", 24370403011232, 810606451),
            Triple("+792959-05-03T08:18:31.616194572Z", 24961212490711, 616194572),
            Triple("-261823-02-16T03:17:35.085815500Z", -8324498983345, 85815500),
            Triple("+931062-03-22T17:04:54.135075640Z", 29319318637494, 135075640),
            Triple("+623320-01-26T03:08:05.121769356Z", 19607914264085, 121769356),
            Triple("+322804-03-06T11:31:24.788006817Z", 10124548774284, 788006817),
            Triple("-784322-04-03T21:25:19.666588404Z", -24812970806081, 666588404),
            Triple("+403293-01-07T05:59:41.601460200Z", 12664531288781, 601460200),
            Triple("-835821-06-01T00:52:15.782852248Z", -26438117296065, 782852248),
            Triple("+222483-07-15T08:29:55.019931345Z", 6958735086595, 19931345),
            Triple("-663595-09-05T04:36:24.110433196Z", -21003181356216, 110433196),
            Triple("+166626-02-15T22:16:34.070665743Z", 5196045449794, 70665743),
            Triple("-517158-01-02T22:52:24.155574933Z", -16382097162456, 155574933),
            Triple("+850155-01-02T10:25:31.349473798Z", 26766133467931, 349473798),
            Triple("-967697-04-25T20:43:33.328060156Z", -30599725115787, 328060156),
            Triple("+437131-04-26T07:32:58.134219875Z", 13732364705578, 134219875),
            Triple("+372920-11-25T13:38:22.852562723Z", 11706079786702, 852562723),
            Triple("+169255-09-07T11:28:18.481625778Z", 5279026303698, 481625778),
            Triple("-980786-08-18T17:05:22.581779094Z", -31012764044078, 581779094),
            Triple("+182945-05-25T20:39:24.545585221Z", 5711031952764, 545585221),
            Triple("+300811-12-15T02:53:38.676752671Z", 9430541175218, 676752671),
            Triple("-807816-01-18T18:04:26.291749218Z", -25554376389334, 291749218),
            Triple("-53033-12-30T22:02:01.398533618Z", -1735695568679, 398533618),
            Triple("-354903-06-14T10:08:46.111648055Z", -11261809864274, 111648055),
            Triple("+842009-03-11T23:58:06.537554993Z", 26509076495886, 537554993),
            Triple("-391976-11-09T04:16:17.862484469Z", -12431707962223, 862484469),
            Triple("-733019-10-28T17:07:13.450343935Z", -23193986539967, 450343935),
            Triple("+595280-03-05T23:36:27.765851400Z", 18723060833787, 765851400),
            Triple("-930296-07-17T03:33:33.094509320Z", -29419456335987, 94509320),
            Triple("+609508-02-29T10:58:02.703241053Z", 19172052557882, 703241053),
            Triple("+996233-06-25T06:01:55.647461964Z", 31375924927315, 647461964),
            Triple("-93200-12-06T21:29:56.140938343Z", -3003245692204, 140938343),
            Triple("+794143-07-02T09:49:35.585085194Z", 24998581100975, 585085194),
            Triple("-783550-12-31T17:10:16.577723428Z", -24788585371784, 577723428),
            Triple("-240168-11-03T17:22:09.108424624Z", -7641110702271, 108424624),
            Triple("+613419-02-15T12:00:07.012460989Z", 19295470641607, 12460989),
            Triple("-521405-03-25T02:03:46.552711998Z", -16516112536574, 552711998),
            Triple("-938829-01-22T16:48:43.582709371Z", -29688747030677, 582709371),
            Triple("+916785-05-16T21:54:45.983221956Z", 28868784818085, 983221956),
            Triple("+482425-06-09T04:24:32.683186155Z", 15161709183872, 683186155),
            Triple("+622585-08-20T05:45:52.555088343Z", 19584737819152, 555088343),
            Triple("-451048-11-02T01:49:29.076392891Z", -14295840847831, 76392891),
            Triple("+721083-09-17T00:31:34.648020241Z", 22693036811494, 648020241),
            Triple("+235979-10-28T12:07:33.706273641Z", 7384636728453, 706273641),
            Triple("+285234-04-12T18:30:25.215363003Z", 8938957285825, 215363003),
            Triple("-917176-03-10T10:03:25.943265324Z", -29005440213395, 943265324),
            Triple("-381932-09-05T02:47:17.004960541Z", -12114755529163, 4960541),
            Triple("-52158-11-11T09:38:45.489915403Z", -1708087530075, 489915403),
            Triple("-584290-11-15T20:15:24.377620606Z", -18500551127076, 377620606),
            Triple("-645616-05-05T17:36:59.941608628Z", -20435829488581, 941608628),
            Triple("+794405-06-22T21:08:20.853641989Z", 25006848239300, 853641989),
            Triple("+986590-08-01T05:15:25.827177433Z", 31071624470125, 827177433),
            Triple("+527158-02-06T12:34:35.088546391Z", 16573335654875, 88546391),
            Triple("-513116-05-01T07:28:44.448204123Z", -16254533665876, 448204123),
            Triple("+397065-10-19T21:59:05.831855226Z", 12468019211945, 831855226),
            Triple("+312769-04-26T11:33:07.802217284Z", 9807879123187, 802217284),
            Triple("+682473-04-14T01:00:38.067076018Z", 21474609498038, 67076018),
            Triple("+731560-02-15T02:15:06.599802467Z", 23023640456106, 599802467),
            Triple("-877354-10-27T22:55:02.723751549Z", -27748759338298, 723751549),
            Triple("-746193-01-02T07:19:56.258497483Z", -23609743807204, 258497483),
            Triple("-822112-07-28T08:55:19.319285417Z", -26005498038281, 319285417),
            Triple("-400365-04-30T00:05:51.210582736Z", -12696455980449, 210582736),
            Triple("+436254-07-11T18:08:06.937065549Z", 13704695921286, 937065549),
            Triple("-340854-01-07T03:17:32.367173472Z", -10818479997748, 367173472),
            Triple("-985221-04-25T22:57:01.511559459Z", -31152729085379, 511559459),
            Triple("+859861-09-01T02:21:20.289341591Z", 27072446149280, 289341591),
            Triple("-0131-07-16T10:47:54.756333457Z", -66284140326, 756333457),
            Triple("-327041-11-18T22:55:21.885337272Z", -10382556503079, 885337272),
            Triple("-268616-05-06T10:27:54.420166505Z", -8538858480726, 420166505),
            Triple("-228012-05-16T15:26:54.680432991Z", -7257519160386, 680432991),
            Triple("+857168-09-12T13:29:36.945689251Z", 26987464272576, 945689251),
            Triple("-974181-04-12T08:47:35.627678735Z", -30804341526745, 627678735),
            Triple("-435700-10-20T22:33:13.897477229Z", -13811505874007, 897477229),
            Triple("-507467-01-19T23:06:05.156792267Z", -16076277276835, 156792267),
            Triple("-382257-11-19T08:00:10.407963305Z", -12125005142390, 407963305),
            Triple("+83082-01-04T20:18:56.409867424Z", 2559647852336, 409867424),
            Triple("-916839-09-12T22:45:39.091941363Z", -28994789466861, 91941363),
            Triple("-147771-05-07T08:31:34.950238979Z", -4725358615706, 950238979),
            // random enormous values queried from java.time
            Triple("+639727757-10-17T17:26:30.359003681Z", 20187795978609990, 359003681),
            Triple("-375448814-11-11T03:04:48.637504595Z", -11848082341899312, 637504595),
            Triple("+99162559-10-16T11:21:05.057717803Z", 3129205972303265, 57717803),
            Triple("-826813174-05-22T21:35:39.018693830Z", -26091765799784661, 18693830),
            Triple("+254125623-04-28T11:42:22.659957596Z", 8019367929949342, 659957596),
            Triple("+540978343-01-03T20:31:36.945404442Z", 17071565436102696, 945404442),
            Triple("+988277529-06-06T13:25:41.942771350Z", 31186964391657941, 942771350),
            Triple("+566487909-09-04T08:09:46.007490076Z", 17876569606963786, 7490076),
            Triple("+442225124-02-16T18:23:29.975096773Z", 13955214848034209, 975096773),
            Triple("-399250586-02-19T22:58:29.585918917Z", -12599193741267691, 585918917),
            Triple("-190217791-04-28T00:49:29.270751921Z", -6002755857213031, 270751921),
            Triple("-716173704-05-24T22:05:33.639928802Z", -22600321355469267, 639928802),
            Triple("+910504788-10-26T05:23:43.517192887Z", 28732693749312223, 517192887),
            Triple("+515896807-08-17T06:36:25.012343642Z", 16280068627982185, 12343642),
            Triple("-623794742-03-20T17:09:07.396143995Z", -19685122891483853, 396143995),
            Triple("-416781718-10-06T01:41:32.866307162Z", -13152422812544308, 866307162),
            Triple("+287346593-09-30T23:30:55.109337183Z", 9067720499134255, 109337183),
            Triple("-819839065-09-06T07:25:58.953784983Z", -25871684167672442, 953784983),
            Triple("+673467211-06-05T02:15:40.712392732Z", 21252510297310540, 712392732),
            Triple("+982441727-04-13T12:12:06.776817565Z", 31002804263391126, 776817565),
            Triple("0010-09-11T23:47:55Z", -61829655125L, 0),
        )) {
            val instant = Instant.parse(str)
            assertEquals(
                Instant.fromEpochSeconds(seconds, nanos), instant,
                "Parsed $instant from $str, with Unix time = `$seconds + 10^-9 * $nanos`"
            )
            assertEquals(instant, Instant.parseOrNull(str), "results of Instant.parse and parseOrNull should be the same for $str")
            assertEquals(str, instant.toString())
        }
        // non-canonical strings are parsed as well, but formatted differently
        for ((str, seconds, nanos) in arrayOf(
            // upper, lower case, trailing zeros
            Triple("2024-07-15T14:06:29.461245000z", 1721052389, 461245000),
            Triple("2024-07-15t14:06:29.4612450z", 1721052389, 461245000),
            // current time
            Triple("2024-07-15T16:06:29.461245691+02:00", 1721052389, 461245691),
        )) {
            val instant = Instant.parse(str)
            assertEquals(
                seconds.toLong() * 1000 + nanos / 1000000, instant.toEpochMilliseconds(),
                "Parsed $instant from $str, with Unix time = `$seconds + 10^-9 * $nanos`"
            )
            assertEquals(instant, Instant.parseOrNull(str), "results of Instant.parse and parseOrNull should be the same for $str")
        }
    }

    @Test
    fun nonParseableInstantStrings() {
        for (nonIsoString in listOf(
            // empty string
            "",
            // a non-empty but clearly unsuitable string
            "x",
            // something other than a sign at the beginning
            " 1970-01-01T00:00:00Z",
            // too many digits for the year
            "+1234567890-01-01T00:00:00Z",
            "-1234567890-01-01T00:00:00Z",
            // not enough padding for the year
            "003-01-01T00:00:00Z",
            "-003-01-01T00:00:00Z",
            // a plus sign even though there is only 4 digits
            "+1970-01-01T00:00:00Z",
            // too many digits without padding
            "11970-01-01T00:00:00Z",
            // incorrect separators between the components
            "1970/01-01T00:00:00Z",
            "1970-01/01T00:00:00Z",
            "1970-01-01 00:00:00Z",
            "1970-01-01T00-00:00Z",
            "1970-01-01T00:00-00Z",
            // non-digits where digits are expected
            "1970-X1-01T00:00:00Z",
            "1970-1X-01T00:00:00Z",
            "1970-11-X1T00:00:00Z",
            "1970-11-1XT00:00:00Z",
            "1970-11-10TX0:00:00Z",
            "1970-11-10T0X:00:00Z",
            "1970-11-10T00:X0:00Z",
            "1970-11-10T00:0X:00Z",
            "1970-11-10T00:00:X0Z",
            "1970-11-10T00:00:0XZ",
            // a non-ascii digit
            "1970-11-10T00:00:0٩Z",
            // not enough components
            "1970-11-10T00:00Z",
            // not enough components, even if the length is sufficient
            "1970-11-10T00:00+01:15",
            // a dot without any fraction of the second following it
            "1970-11-10T00:00:00.Z",
            // too many digits in the fraction of the second
            "1970-11-10T00:00:00.1234567890Z",
            // out-of-range values
            "1970-00-10T00:00:00Z",
            "1970-13-10T00:00:00Z",
            "1970-01-32T00:00:00Z",
            "1970-02-29T00:00:00Z",
            "1972-02-30T00:00:00Z",
            "2000-02-30T00:00:00Z",
            "2100-02-29T00:00:00Z",
            "2004-02-30T00:00:00Z",
            "2005-02-29T00:00:00Z",
            "2005-04-31T00:00:00Z",
            "2005-04-01T24:00:00Z",
            "2005-04-01T00:60:00Z",
            "2005-04-01T00:00:60Z",
            // leap second
            "1970-01-01T23:59:60Z",
            // lack of padding
            "1970-1-10T00:00:00+05:00",
            "1970-10-1T00:00:00+05:00",
            "1970-10-10T0:00:00+05:00",
            "1970-10-10T00:0:00+05:00",
            "1970-10-10T00:00:0+05:00",
            // no offset
            "1970-02-03T04:05:06.123456789",
            // some invalid single-character offsets
            "1970-02-03T04:05:06.123456789A",
            "1970-02-03T04:05:06.123456789+",
            "1970-02-03T04:05:06.123456789-",
            // too many components in the offset
            "1970-02-03T04:05:06.123456789+03:02:01:00",
            "1970-02-03T04:05:06.123456789+03:02:01.02",
            // single-digit offset
            "1970-02-03T04:05:06.123456789+3",
            // incorrect sign in the offset
            "1970-02-03T04:05:06.123456789 03",
            // non-digits in the offset
            "1970-02-03T04:05:06.123456789+X3",
            "1970-02-03T04:05:06.123456789+1X",
            "1970-02-03T04:05:06.123456789+X3:12",
            "1970-02-03T04:05:06.123456789+1X:12",
            "1970-02-03T04:05:06.123456789+X3:12",
            "1970-02-03T04:05:06.123456789+13:X2",
            "1970-02-03T04:05:06.123456789+13:1X",
            "1970-02-03T04:05:06.123456789+X3:12:59",
            "1970-02-03T04:05:06.123456789+1X:12:59",
            "1970-02-03T04:05:06.123456789+X3:12:59",
            "1970-02-03T04:05:06.123456789+13:X2:59",
            "1970-02-03T04:05:06.123456789+13:1X:59",
            "1970-02-03T04:05:06.123456789+13:12:X9",
            "1970-02-03T04:05:06.123456789+13:12:5X",
            // incorrect separators in the offset
            "1970-02-03T04:05:06.123456789+13/12",
            "1970-02-03T04:05:06.123456789+13/12:59",
            "1970-02-03T04:05:06.123456789+13:12/59",
            "1970-02-03T04:05:06.123456789+0130",
            "1970-02-03T04:05:06.123456789-0130",
            // incorrect field length
            "1970-02-03T04:05:06.123456789-18:001",
            // out-of-range offsets
            "1970-02-03T04:05:06.123456789+18:12:59",
            "1970-02-03T04:05:06.123456789-18:12:59",
            "1970-02-03T04:05:06.123456789+18:00:01",
            "1970-02-03T04:05:06.123456789-18:00:01",
            "1970-02-03T04:05:06.123456789+18:01",
            "1970-02-03T04:05:06.123456789-18:01",
            "1970-02-03T04:05:06.123456789+19",
            "1970-02-03T04:05:06.123456789-19",
            // out-of-range fields of the offset
            "1970-02-03T04:05:06.123456789+01:12:60",
            "1970-02-03T04:05:06.123456789-01:12:60",
            "1970-02-03T04:05:06.123456789+01:60",
            "1970-02-03T04:05:06.123456789-01:60",
            // lack of padding in the offset
            "1970-02-03T04:05:06.123456789+1:12:50",
            "1970-02-03T04:05:06.123456789+01:2:60",
            "1970-02-03T04:05:06.123456789+01:12:6",
            // padding characters in the end
            "2005-04-01T00:59:00Zzz",
            // year is too long
            "10000000005-04-01T00:59:00Z",
            "2000000000-04-01T00:59:00Z",
            // too many digits in a fractional part
            "2005-04-01T00:59:00.00123456789Z",
        )) {
            assertInvalidFormat(nonIsoString) { Instant.parse(nonIsoString) }
            assertNull(Instant.parseOrNull(nonIsoString), nonIsoString)
        }
        // this string represents an Instant that is currently larger than Instant.MAX any of the implementations:
        "+1000000001-12-31T23:59:59.000000000Z".let {
            assertInvalidFormat { Instant.parse(it) }
            assertNull(Instant.parseOrNull(it))
        }
    }

    @Test
    fun parseStringsWithOffsets() {
        val strings = arrayOf(
            Pair("2020-01-01T00:01:01.02+18:00", "2019-12-31T06:01:01.020Z"),
            Pair("2020-01-01T00:01:01.123456789-17:59:59", "2020-01-01T18:01:00.123456789Z"),
            Pair("2020-01-01T00:01:01.010203040+17:59:59", "2019-12-31T06:01:02.010203040Z"),
            Pair("2020-01-01T00:01:01.010203040+17:59", "2019-12-31T06:02:01.010203040Z"),
            Pair("2020-01-01T00:01:01+00", "2020-01-01T00:01:01Z"),
        )
        strings.forEach { (str, strInZ) ->
            val instant = Instant.parse(str)
            assertEquals(Instant.parse(strInZ), instant, str)
            assertEquals(instant, Instant.parseOrNull(str), str)
            assertEquals(strInZ, instant.toString(), str)
        }
        val invalidStringsWithOffsets = listOf(
            "2020-01-01T00:01:01+18:01",
            "2020-01-01T00:01:01+1801",
            "2020-01-01T00:01:01+0",
            "2020-01-01T00:01:01+",
            "2020-01-01T00:01:01",
            "2020-01-01T00:01:01+000000",
        )
        invalidStringsWithOffsets.forEach {
            assertInvalidFormat { Instant.parse(it) }
            assertNull(Instant.parseOrNull(it))
        }

        val instants = listOf(
            Instant.DISTANT_FUTURE,
            Instant.DISTANT_PAST,
            Instant.fromEpochSeconds(0, 0),
            Instant.parse("2020-01-02T03:04:05.6789Z"),
            Instant.MAX,
            Instant.MIN,
        )

        val offsets = listOf(
            0 to "Z",
            3 * 3600 + 12 * 60 + 14 to "+03:12:14",
            - 3 * 3600 - 12 * 60 - 14 to "-03:12:14",
            2 * 3600 + 35 * 60 to "+02:35",
            - 2 * 3600 - 35 * 60 to "-02:35",
            4 * 3600 to "+04",
            - 4 * 3600 to "-04",
        )

        for (instant in instants) {
            for ((offsetSeconds, offsetString) in offsets) {
                if (instant == Instant.MAX && offsetSeconds < 0 ||
                    instant == Instant.MIN && offsetSeconds > 0
                ) continue
                val stringToParse = "${instant.toString().dropLast(1)}$offsetString"
                val newInstant = Instant.parse(stringToParse)
                assertEquals(newInstant, instant.minus(offsetSeconds.seconds))
                assertEquals(newInstant, Instant.parseOrNull(stringToParse))
            }
        }
    }

}

private inline fun <T> assertInvalidFormat(message: String? = null, f: () -> T) {
    assertFailsWith<IllegalArgumentException>(message) {
        val result = f()
        fail(result.toString())
    }
}
